'''
Author: Han Ruiqian
Date: 2022-06-09 09:17:45
LastEditors: Han Ruiqian
LastEditTime: 2022-06-15 09:37:57
'''

import pandas as pd

import pyecharts.options as opts
from pyecharts.charts import Funnel
from pyecharts.charts import Page


# 读取csv文件
def read_file(filename1, filename2):
    "读取csv文件"
    data_up = pd.read_csv(filename1)
    # print(data_up)
    data_video = pd.read_csv(filename2)
    # print(data_video)
    return data_up, data_video


# test_all=pd.DataFrame(data=data_all)#将数据放进表格
# test_all.to_csv('test_all.csv') #数据存入csv,存储位置及文件名称

# 数据预处理
def data_preprocessing(data_up, data_video):
    "数据预处理"
    # 去重操作
    data_up.drop_duplicates(subset=['mid'], inplace=True)
    data_video.drop_duplicates(subset=['video_title', 'video_up_mid'], inplace=True)
    # print(data_video)

    # 删除含有缺失值的行
    data_up.dropna(axis=0, how='any', inplace=True)
    data_video.dropna(axis=0, how='any', inplace=True)


# 数据筛选
def data_select(data_up, data_video, key_word):
    "筛选出视频标签含有关键字的up的数据"
    # 按照up主id合并两个表
    data_all = pd.merge(data_up, data_video, left_on='mid', right_on='video_up_mid', how='left')
    # print(data_all)

    # 按照关键字筛选视频标签
    data_all_copy = data_all.copy()
    data = pd.DataFrame(data_all_copy['video_tags'])
    data_all_copy = data_all_copy[(data['video_tags'].astype(str).str.contains(key_word))]
    # print(data_all_copy)

    # 对视频按照up主id去重，得到视频含有关键字标签的up
    data_all_copy.drop_duplicates(subset=['video_up_mid'], inplace=True)
    data_new = pd.DataFrame(data_all_copy, columns=["video_up_mid"])
    # print(data_new)
    # print(data_video)

    # 对所有的数据按照筛选出的up主进行过滤
    data_result = pd.merge(data_new, data_all, how='left')
    return data_result

    # 输出到文件查看数据信息
    # print("输出到文件")
    # test=pd.DataFrame(data=data_result)#将数据放进表格
    # test.to_csv('test.csv') #数据存入csv,存储位置及文件名称


# 拆分up主
def split_by_up(data):
    "将每个up主拆分成一个表格,返回一个存储up主id和up主数据的表格"
    data_copy = data.copy()
    list_mid = data_copy['mid'].unique()
    # print(list_mid)
    df_up = pd.DataFrame(columns=['up_id', 'up_data'])
    # print(df_up)
    for i in range(len(list_mid)):
        data_every_up = data_copy.copy()
        data_every_up = data_every_up[(data_copy["mid"].isin([list_mid[i]]))]
        # print(data_every_up)
        # print([list_mid[i],data_every_up])
        data_to_add = pd.DataFrame({'up_id': [list_mid[i]], 'up_data': [data_every_up]})
        df_up = pd.concat([df_up, data_to_add]).reset_index(drop=True)
        # df_up=df_up.append(pd.DataFrame({'up_id':[list_mid[i]],'up_data':[data_every_up]}),ignore_index=True)
    # print(df_up)
    return df_up


# 计算每个up主的I值(互动率)
# I = （总弹幕数+总评论数）/总播放量/统计范围内的视频数量
def get_I(data):
    data_copy = data.copy()
    dfs = data_copy['up_data']
    # print(len(dfs))
    list_mid = data_copy['up_id'].unique()
    df_result = pd.DataFrame(columns=['up_id', 'sum_danmaku', 'sum_reply', 'sum_play', 'video_number', 'I'])
    for i in range(len(dfs)):
        df = dfs[i]
        # print(df)

        sum_danmaku = df['video_danmaku'].sum()
        sum_reply = df['video_reply'].sum()
        sum_play = df['video_play'].sum()
        video_number = len(df)
        I = 10000 * (sum_danmaku + sum_reply) / sum_play / video_number
        data_to_add = pd.DataFrame({'up_id': list_mid[i],
                                    'sum_danmaku': sum_danmaku,
                                    'sum_reply': sum_reply,
                                    'sum_play': sum_play,
                                    'video_number': video_number,
                                    'I': I}, index=[0])
        df_result = pd.concat([df_result, data_to_add]).reset_index(drop=True)
        # df_result = df_result.append({'up_id':list_mid[i],
        #                               'sum_danmaku':sum_danmaku,
        #                               'sum_reply':sum_reply,
        #                               'sum_play':sum_play,
        #                               'video_number':video_number,
        #                               'I':I},
        #                              ignore_index=True)
        # concat([df1,df2]) 等价于 df1.append(df2)
    # print(df_result)
    data_I = df_result[['up_id', 'I']]
    # print(data_I)
    return data_I


# 计算每个up主的F值(每个视频的平均发布周期)
# F=（统计范围内最晚发布视频时间-最早发布视频时间）/发布视频数量
def get_F(data):
    data_copy = data.copy()
    dfs = data_copy['up_data']
    # print(len(dfs))
    list_mid = data_copy['up_id'].unique()
    df_result = pd.DataFrame(columns=['up_id', 'video_created_min', 'video_created_max', 'video_number', 'F'])
    for i in range(len(dfs)):
        df = dfs[i]
        # 按照时间排序
        df.sort_values(by="video_created", inplace=True, ascending=True)
        # 取得时间最大最小值
        video_create_min = df.iloc[0]['video_created']
        video_create_max = df.iloc[-1]['video_created']
        video_number = len(df)
        F = (video_create_max - video_create_min) / video_number / 100000
        data_to_add = pd.DataFrame({'up_id': list_mid[i],
                                    'video_created_min': video_create_min,
                                    'video_created_max': video_create_max,
                                    'video_number': video_number,
                                    'F': F}, index=[0])
        df_result = pd.concat([df_result, data_to_add]).reset_index(drop=True)
    # print(df_result)
    data_F = df_result[['up_id', 'F']]
    # print(data_F)
    return (data_F)


# 计算每个up主的L值(视频的平均点赞率)
# L=（点赞数*1+投币数*2+收藏数*3+分享数*4）/播放量/视频数量
def get_L(data):
    data_copy = data.copy()
    dfs = data_copy['up_data']
    # print(len(dfs))
    list_mid = data_copy['up_id'].unique()
    df_result = pd.DataFrame(
        columns=['up_id', 'sum_like', 'sum_coin', 'sum_fav', 'sum_share', 'sum_play', 'video_number', 'L'])
    for i in range(len(dfs)):
        df = dfs[i]
        sum_like = df['video_like'].sum()
        sum_coin = df['video_coin'].sum()
        sum_fav = df['video_fav'].sum()
        sum_share = df['video_share'].sum()
        sum_play = df['video_play'].sum()
        video_number = len(df)
        L = 1000 * (sum_like * 1 + sum_coin * 2 + sum_fav * 3 + sum_share * 4) / sum_play / video_number
        # 为了便于排名，将L值*1000
        data_to_add = pd.DataFrame({'up_id': list_mid[i],
                                    'sum_like': sum_like,
                                    'sum_coin': sum_coin,
                                    'sum_fav': sum_fav,
                                    'sum_share': sum_share,
                                    'sum_play': sum_play,
                                    'video_number': video_number,
                                    'L': L}, index=[0])
        df_result = pd.concat([df_result, data_to_add]).reset_index(drop=True)
    # print(df_result)
    data_L = df_result[['up_id', 'L']]
    # print(data_L)
    # print("得到L值")
    return data_L


# 计算IFL分数
# bins参数代表按照什么区间进行分组
# labels和bins切分的数组前后呼应,给每个分组打标签
# right表示了右侧区间是开还是闭，即包不包括右边的数值，如果设置成False，就代表[0,30)
def get_score(data):
    data['I_SCORE'] = pd.cut(data['I'], bins=[0, 0.3, 1.0, 3.0, 1000],
                             labels=[1, 2, 3, 4], right=False).astype(float)

    data['F_SCORE'] = pd.cut(data['F'], bins=[0, 3, 6, 10, 20, 1000],
                             labels=[5, 4, 3, 2, 1], right=False).astype(float)

    data['L_SCORE'] = pd.cut(data['L'], bins=[0, 2, 4, 7, 1000],
                             labels=[1, 2, 3, 4], right=False).astype(float)
    # print("得到IFL分数")
    return data


# 利用IFL分数对up主进行设置人群数值，用于对up主分类
def classify(data):
    data['I是否大于平均值'] = (data['I_SCORE'] > data['I_SCORE'].mean()) * 1
    data['F是否大于平均值'] = (data['F_SCORE'] > data['F_SCORE'].mean()) * 1
    data['L是否大于平均值'] = (data['L_SCORE'] > data['L_SCORE'].mean()) * 1
    data['人群数值'] = (data['I是否大于平均值'] * 100) + (data['F是否大于平均值'] * 10) + (data['L是否大于平均值'] * 1)
    return data


# 根据人群数值进行人群类型标签和排名的转换
def transform_label(number):
    if number == 111:
        label = '高价值up主'
        rank = 1
    elif number == 101:
        label = '高价值拖更up主'
        rank = 2
    elif number == 11:
        label = '高质量内容高深up主'
        rank = 3
    elif number == 1:
        label = '高质量内容高深拖更up主'
        rank = 4
    elif number == 110:
        label = '接地气活跃up主'
        rank = 5
    elif number == 10:
        label = '活跃up主'
        rank = 6
    elif number == 100:
        label = '接地气up主'
        rank = 7
    elif number == 0:
        label = '还在成长的up主'
        rank = 8
    return label, rank


# 根据人群类型、排名和up主信息得到包含所有分数的表
def get_last_df(data, data_up):
    data[['人群类型', '排名']] = data.apply(lambda data: transform_label(data['人群数值']), axis=1, result_type='expand')
    data = data.merge(data_up, how='left', left_on='up_id', right_on='mid')
    # print(data)
    return data


# 排名规则，先根据人群类型进行排名，然后根据L值从高到低排名
def rank_method(df):
    df = df[['up_id', 'up_name', 'L', '排名']].copy()
    # print(df)
    df.sort_values(by=['排名', 'L'], inplace=True, ascending=[True, False])
    df = df.reset_index(drop=True)
    # print(df)
    return df


# 数据可视化，以漏斗图形式展示
def photo(data):
    x_data = pd.Series(data['up_name']).tolist()
    y_data = data.index.tolist()

    data = [[x_data[i], y_data[i] + 1] for i in y_data]
    page = (
        Funnel(init_opts=opts.InitOpts(width="800px", height="400px"))
            .add(
            series_name="",
            data_pair=data,
            gap=2,
            tooltip_opts=opts.TooltipOpts(trigger="item", formatter="第{c}名 <br/>{b}"),
            label_opts=opts.LabelOpts(is_show=True, position="inside"),
            itemstyle_opts=opts.ItemStyleOpts(border_color="#fff", border_width=1),
            sort_='ascending'
        )
            .set_global_opts(
            title_opts=opts.TitleOpts(title="up主排名", subtitle="从下到上排名依次升高", pos_bottom=10, pos_left='center'))
    )
    return page


# 排名主函数
def get_ranking(key_word):
    filename1 = "up.csv"
    filename2 = "data.csv"
    # 文件读取
    data_up, data_video = read_file(filename1, filename2)

    # 数据预处理
    data_preprocessing(data_up, data_video)

    # 数据按照关键字筛选
    data_result = data_select(data_up, data_video, key_word)

    # 将每个up主拆分为一个表格，返回一个存放up主表格的数组
    up_df = split_by_up(data_result)

    # 得到IFL值和分数表格
    up_score = get_I(up_df)
    up_score = up_score.merge(get_F(up_df), how='inner')
    up_score = up_score.merge(get_L(up_df), how='inner')
    up_score = get_score(up_score)

    # 根据IFL值对up主进行人群分类
    up_score = classify(up_score)
    up_score = get_last_df(up_score, data_up)

    # 根据排名规则对up主进行排名
    up_score_to_photo = rank_method(up_score)

    # 数据可视化
    page = photo(up_score_to_photo)

    return up_score_to_photo['up_id'].tolist(), page
